{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "sDF0s1Y5ckw3"
      },
      "source": [
        "# Week 7 Exercise: Fine-Tuned LLaMA 3.1 Price Prediction\n",
        "\n",
        "Evaluating fine-tuned LLaMA 3.1 8B model for product price estimation\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "1ZWMy31Wckw5"
      },
      "outputs": [],
      "source": [
        "# Install required libraries for model inference\n",
        "%pip install -q --upgrade torch==2.5.1+cu124 torchvision==0.20.1+cu124 torchaudio==2.5.1+cu124 --index-url https://download.pytorch.org/whl/cu124\n",
        "%pip install -q --upgrade requests==2.32.3 bitsandbytes==0.46.0 transformers==4.48.3 accelerate==1.3.0 datasets==3.2.0 peft==0.14.0 trl==0.14.0 matplotlib wandb"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "7myHMoQickw7"
      },
      "outputs": [],
      "source": [
        "# Imports\n",
        "import os\n",
        "import re\n",
        "import torch\n",
        "import numpy as np\n",
        "import matplotlib.pyplot as plt\n",
        "from tqdm import tqdm\n",
        "from google.colab import userdata\n",
        "from huggingface_hub import login\n",
        "from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig, set_seed\n",
        "from datasets import load_dataset\n",
        "from peft import PeftModel\n",
        "from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score\n",
        "\n",
        "%matplotlib inline\n",
        "set_seed(42)\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "m4gRocgWckw8"
      },
      "outputs": [],
      "source": [
        "# Configuration\n",
        "BASE_MODEL = \"meta-llama/Meta-Llama-3.1-8B\"\n",
        "HF_USER = \"ed-donner\"\n",
        "PROJECT_NAME = \"pricer\"\n",
        "RUN_NAME = \"2024-09-13_13.04.39\"\n",
        "REVISION = \"e8d637df551603dc86cd7a1598a8f44af4d7ae36\"\n",
        "\n",
        "FINETUNED_MODEL = f\"{HF_USER}/{PROJECT_NAME}-{RUN_NAME}\"\n",
        "DATASET_NAME = f\"{HF_USER}/pricer-data\"\n",
        "QUANT_4_BIT = False\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "M3kiaJCCckw-"
      },
      "outputs": [],
      "source": [
        "# Login to HuggingFace\n",
        "hf_token = userdata.get('HF_TOKEN')\n",
        "login(hf_token, add_to_git_credential=True)\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "dcM5HkLlckw-",
        "outputId": "07be7fab-e532-4fdc-9785-ed18327a7790"
      },
      "outputs": [],
      "source": [
        "# Load dataset with token\n",
        "hf_token = userdata.get('HF_TOKEN')\n",
        "dataset = load_dataset(DATASET_NAME, token=hf_token)\n",
        "train = dataset['train']\n",
        "test = dataset['test']\n",
        "\n",
        "print(f\"Train: {len(train)} samples\")\n",
        "print(f\"Test: {len(test)} samples\")\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "wPKrI3Fsckw_",
        "outputId": "78c468ce-360b-4656-deb4-ecd6a0b9da95"
      },
      "outputs": [],
      "source": [
        "# Configure quantization\n",
        "if QUANT_4_BIT:\n",
        "    quant_config = BitsAndBytesConfig(\n",
        "        load_in_4bit=True,\n",
        "        bnb_4bit_use_double_quant=True,\n",
        "        bnb_4bit_compute_dtype=torch.bfloat16,\n",
        "        bnb_4bit_quant_type=\"nf4\"\n",
        "    )\n",
        "else:\n",
        "    quant_config = BitsAndBytesConfig(\n",
        "        load_in_8bit=True,\n",
        "        bnb_8bit_compute_dtype=torch.bfloat16\n",
        "    )\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 66,
          "referenced_widgets": [
            "3eab81e8c5c44df5b21c10d78aea035e",
            "775e8529c6fd4e5bb9193320eebe6a68",
            "511604ad47864134ad7e640ad607ec17",
            "f5455d8b1b4c48dcb34708d81a98627c",
            "e9d4a2a7fbf04d7db88c4001a3b1a8f4",
            "93fef72b736b4bc8be4d606fb3f7b7bd",
            "490ecf18e53746baa6f03a49c3c2525f",
            "525c4e8349d94f258a3088c7950f2f0c",
            "5fd6337837964ecd9ace1dc5fce2d522",
            "fffb14089cdf49d9bbb1f3b5b24d89be",
            "e6e425561bae48dca4dc9e6a00507a1d"
          ]
        },
        "id": "-ss96JyMckxA",
        "outputId": "2b8dc3ad-fac9-44b1-a9c2-137c637016be"
      },
      "outputs": [],
      "source": [
        "# Load tokenizer\n",
        "tokenizer = AutoTokenizer.from_pretrained(BASE_MODEL, trust_remote_code=True)\n",
        "tokenizer.pad_token = tokenizer.eos_token\n",
        "tokenizer.padding_side = \"right\"\n",
        "\n",
        "# Load base model with quantization\n",
        "base_model = AutoModelForCausalLM.from_pretrained(\n",
        "    BASE_MODEL,\n",
        "    quantization_config=quant_config,\n",
        "    device_map=\"auto\",\n",
        ")\n",
        "base_model.generation_config.pad_token_id = tokenizer.pad_token_id\n",
        "\n",
        "# Load fine-tuned weights\n",
        "if REVISION:\n",
        "  fine_tuned_model = PeftModel.from_pretrained(base_model, FINETUNED_MODEL, revision=REVISION)\n",
        "else:\n",
        "  fine_tuned_model = PeftModel.from_pretrained(base_model, FINETUNED_MODEL)\n",
        "\n",
        "print(f\"Model loaded - Memory: {fine_tuned_model.get_memory_footprint() / 1e6:.1f} MB\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "JfM2YRFKckxA"
      },
      "outputs": [],
      "source": [
        "# Helper functions\n",
        "import math\n",
        "\n",
        "def extract_price(text):\n",
        "    if \"Price is $\" in text:\n",
        "        contents = text.split(\"Price is $\")[1]\n",
        "        contents = contents.replace(',', '')\n",
        "        match = re.search(r\"[-+]?\\d*\\.\\d+|\\d+\", contents)\n",
        "        return float(match.group()) if match else 0.0\n",
        "    return 0.0\n",
        "\n",
        "def predict_price(prompt):\n",
        "    inputs = tokenizer.encode(prompt, return_tensors=\"pt\").to(fine_tuned_model.device)\n",
        "    attention_mask = torch.ones(inputs.shape, device=fine_tuned_model.device)\n",
        "\n",
        "    with torch.no_grad():\n",
        "        outputs = fine_tuned_model.generate(\n",
        "            inputs,\n",
        "            attention_mask=attention_mask,\n",
        "            max_new_tokens=10,\n",
        "            num_return_sequences=1\n",
        "        )\n",
        "\n",
        "    response = tokenizer.decode(outputs[0])\n",
        "    return extract_price(response)\n",
        "\n",
        "# Evaluation class\n",
        "GREEN = \"\\033[92m\"\n",
        "YELLOW = \"\\033[93m\"\n",
        "RED = \"\\033[91m\"\n",
        "RESET = \"\\033[0m\"\n",
        "COLOR_MAP = {\"red\": RED, \"orange\": YELLOW, \"green\": GREEN}"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "mGIqVUM9qw3W",
        "outputId": "aee5aea6-cb7d-4e2a-9fdf-c167dc9a0edb"
      },
      "outputs": [],
      "source": [
        "# Improved prediction with weighted top-K\n",
        "import torch.nn.functional as F\n",
        "\n",
        "def predict_price_weighted(prompt, top_k=6):\n",
        "    \"\"\"\n",
        "    Weighted top-K prediction for more robust price estimates.\n",
        "    Takes top K most likely tokens and computes weighted average.\n",
        "    \"\"\"\n",
        "    set_seed(42)\n",
        "    inputs = tokenizer.encode(prompt, return_tensors=\"pt\").to(fine_tuned_model.device)\n",
        "    attention_mask = torch.ones(inputs.shape, device=fine_tuned_model.device)\n",
        "\n",
        "    with torch.no_grad():\n",
        "        outputs = fine_tuned_model(inputs, attention_mask=attention_mask)\n",
        "        next_token_logits = outputs.logits[:, -1, :].to('cpu')\n",
        "\n",
        "    # Get probabilities and top K tokens\n",
        "    next_token_probs = F.softmax(next_token_logits, dim=-1)\n",
        "    top_prob, top_token_id = next_token_probs.topk(top_k)\n",
        "\n",
        "    # Extract prices from top tokens\n",
        "    prices, weights = [], []\n",
        "    for i in range(top_k):\n",
        "        predicted_token = tokenizer.decode(top_token_id[0][i])\n",
        "        probability = top_prob[0][i]\n",
        "        try:\n",
        "            result = float(predicted_token)\n",
        "        except ValueError:\n",
        "            result = 0.0\n",
        "        if result > 0:\n",
        "            prices.append(result)\n",
        "            weights.append(probability.item())\n",
        "\n",
        "    if not prices:\n",
        "        return 0.0\n",
        "\n",
        "    # Compute weighted average\n",
        "    total = sum(weights)\n",
        "    weighted_prices = [price * weight / total for price, weight in zip(prices, weights)]\n",
        "    return sum(weighted_prices)\n",
        "\n",
        "print(\"Weighted top-K prediction ready!\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "IXa9BlhLmIY5"
      },
      "outputs": [],
      "source": [
        "# Evaluation framework\n",
        "class Tester:\n",
        "    def __init__(self, predictor, data, title=None, size=250):\n",
        "        self.predictor = predictor\n",
        "        self.data = data\n",
        "        self.title = title or predictor.__name__.replace(\"_\", \" \").title()\n",
        "        self.size = size\n",
        "        self.guesses = []\n",
        "        self.truths = []\n",
        "        self.errors = []\n",
        "        self.sles = []\n",
        "        self.colors = []\n",
        "\n",
        "    def color_for(self, error, truth):\n",
        "        if error<40 or error/truth < 0.2:\n",
        "            return \"green\"\n",
        "        elif error<80 or error/truth < 0.4:\n",
        "            return \"orange\"\n",
        "        else:\n",
        "            return \"red\"\n",
        "\n",
        "    def run_datapoint(self, i):\n",
        "        datapoint = self.data[i]\n",
        "        guess = self.predictor(datapoint[\"text\"])\n",
        "        truth = datapoint[\"price\"]\n",
        "        error = abs(guess - truth)\n",
        "        log_error = math.log(truth+1) - math.log(guess+1)\n",
        "        sle = log_error ** 2\n",
        "        color = self.color_for(error, truth)\n",
        "        title = datapoint[\"text\"].split(\"\\n\\n\")[1][:20] + \"...\"\n",
        "        self.guesses.append(guess)\n",
        "        self.truths.append(truth)\n",
        "        self.errors.append(error)\n",
        "        self.sles.append(sle)\n",
        "        self.colors.append(color)\n",
        "        print(f\"{COLOR_MAP[color]}{i+1}: Guess: ${guess:,.2f} Truth: ${truth:,.2f} Error: ${error:,.2f} SLE: {sle:,.2f} Item: {title}{RESET}\")\n",
        "\n",
        "    def chart(self, title):\n",
        "        max_error = max(self.errors)\n",
        "        plt.figure(figsize=(12, 8))\n",
        "        max_val = max(max(self.truths), max(self.guesses))\n",
        "        plt.plot([0, max_val], [0, max_val], color='deepskyblue', lw=2, alpha=0.6)\n",
        "        plt.scatter(self.truths, self.guesses, s=3, c=self.colors)\n",
        "        plt.xlabel('Ground Truth')\n",
        "        plt.ylabel('Model Estimate')\n",
        "        plt.xlim(0, max_val)\n",
        "        plt.ylim(0, max_val)\n",
        "        plt.title(title)\n",
        "        plt.show()\n",
        "\n",
        "    def report(self):\n",
        "        average_error = sum(self.errors) / self.size\n",
        "        rmsle = math.sqrt(sum(self.sles) / self.size)\n",
        "        hits = sum(1 for color in self.colors if color==\"green\")\n",
        "        title = f\"{self.title} Error=${average_error:,.2f} RMSLE={rmsle:,.2f} Hits={hits/self.size*100:.1f}%\"\n",
        "        self.chart(title)\n",
        "\n",
        "    def run(self):\n",
        "        self.error = 0\n",
        "        for i in range(self.size):\n",
        "            self.run_datapoint(i)\n",
        "        self.report()\n",
        "\n",
        "    @classmethod\n",
        "    def test(cls, function, data):\n",
        "        cls(function, data).run()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 1000
        },
        "id": "U0FKNqeyckxB",
        "outputId": "9c4c3c72-5955-448c-9e37-521ea54d83c2"
      },
      "outputs": [],
      "source": [
        "# Run evaluation\n",
        "Tester.test(predict_price_weighted, test)\n"
      ]
    }
  ],
  "metadata": {
    "accelerator": "GPU",
    "colab": {
      "gpuType": "T4",
      "provenance": []
    },
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    },
    "language_info": {
      "name": "python"
    },
    "widgets": {
      "application/vnd.jupyter.widget-state+json": {
        "3eab81e8c5c44df5b21c10d78aea035e": {
          "model_module": "@jupyter-widgets/controls",
          "model_module_version": "1.5.0",
          "model_name": "HBoxModel",
          "state": {
            "_dom_classes": [],
            "_model_module": "@jupyter-widgets/controls",
            "_model_module_version": "1.5.0",
            "_model_name": "HBoxModel",
            "_view_count": null,
            "_view_module": "@jupyter-widgets/controls",
            "_view_module_version": "1.5.0",
            "_view_name": "HBoxView",
            "box_style": "",
            "children": [
              "IPY_MODEL_775e8529c6fd4e5bb9193320eebe6a68",
              "IPY_MODEL_511604ad47864134ad7e640ad607ec17",
              "IPY_MODEL_f5455d8b1b4c48dcb34708d81a98627c"
            ],
            "layout": "IPY_MODEL_e9d4a2a7fbf04d7db88c4001a3b1a8f4"
          }
        },
        "490ecf18e53746baa6f03a49c3c2525f": {
          "model_module": "@jupyter-widgets/controls",
          "model_module_version": "1.5.0",
          "model_name": "DescriptionStyleModel",
          "state": {
            "_model_module": "@jupyter-widgets/controls",
            "_model_module_version": "1.5.0",
            "_model_name": "DescriptionStyleModel",
            "_view_count": null,
            "_view_module": "@jupyter-widgets/base",
            "_view_module_version": "1.2.0",
            "_view_name": "StyleView",
            "description_width": ""
          }
        },
        "511604ad47864134ad7e640ad607ec17": {
          "model_module": "@jupyter-widgets/controls",
          "model_module_version": "1.5.0",
          "model_name": "FloatProgressModel",
          "state": {
            "_dom_classes": [],
            "_model_module": "@jupyter-widgets/controls",
            "_model_module_version": "1.5.0",
            "_model_name": "FloatProgressModel",
            "_view_count": null,
            "_view_module": "@jupyter-widgets/controls",
            "_view_module_version": "1.5.0",
            "_view_name": "ProgressView",
            "bar_style": "success",
            "description": "",
            "description_tooltip": null,
            "layout": "IPY_MODEL_525c4e8349d94f258a3088c7950f2f0c",
            "max": 4,
            "min": 0,
            "orientation": "horizontal",
            "style": "IPY_MODEL_5fd6337837964ecd9ace1dc5fce2d522",
            "value": 4
          }
        },
        "525c4e8349d94f258a3088c7950f2f0c": {
          "model_module": "@jupyter-widgets/base",
          "model_module_version": "1.2.0",
          "model_name": "LayoutModel",
          "state": {
            "_model_module": "@jupyter-widgets/base",
            "_model_module_version": "1.2.0",
            "_model_name": "LayoutModel",
            "_view_count": null,
            "_view_module": "@jupyter-widgets/base",
            "_view_module_version": "1.2.0",
            "_view_name": "LayoutView",
            "align_content": null,
            "align_items": null,
            "align_self": null,
            "border": null,
            "bottom": null,
            "display": null,
            "flex": null,
            "flex_flow": null,
            "grid_area": null,
            "grid_auto_columns": null,
            "grid_auto_flow": null,
            "grid_auto_rows": null,
            "grid_column": null,
            "grid_gap": null,
            "grid_row": null,
            "grid_template_areas": null,
            "grid_template_columns": null,
            "grid_template_rows": null,
            "height": null,
            "justify_content": null,
            "justify_items": null,
            "left": null,
            "margin": null,
            "max_height": null,
            "max_width": null,
            "min_height": null,
            "min_width": null,
            "object_fit": null,
            "object_position": null,
            "order": null,
            "overflow": null,
            "overflow_x": null,
            "overflow_y": null,
            "padding": null,
            "right": null,
            "top": null,
            "visibility": null,
            "width": null
          }
        },
        "5fd6337837964ecd9ace1dc5fce2d522": {
          "model_module": "@jupyter-widgets/controls",
          "model_module_version": "1.5.0",
          "model_name": "ProgressStyleModel",
          "state": {
            "_model_module": "@jupyter-widgets/controls",
            "_model_module_version": "1.5.0",
            "_model_name": "ProgressStyleModel",
            "_view_count": null,
            "_view_module": "@jupyter-widgets/base",
            "_view_module_version": "1.2.0",
            "_view_name": "StyleView",
            "bar_color": null,
            "description_width": ""
          }
        },
        "775e8529c6fd4e5bb9193320eebe6a68": {
          "model_module": "@jupyter-widgets/controls",
          "model_module_version": "1.5.0",
          "model_name": "HTMLModel",
          "state": {
            "_dom_classes": [],
            "_model_module": "@jupyter-widgets/controls",
            "_model_module_version": "1.5.0",
            "_model_name": "HTMLModel",
            "_view_count": null,
            "_view_module": "@jupyter-widgets/controls",
            "_view_module_version": "1.5.0",
            "_view_name": "HTMLView",
            "description": "",
            "description_tooltip": null,
            "layout": "IPY_MODEL_93fef72b736b4bc8be4d606fb3f7b7bd",
            "placeholder": "​",
            "style": "IPY_MODEL_490ecf18e53746baa6f03a49c3c2525f",
            "value": "Loading checkpoint shards: 100%"
          }
        },
        "93fef72b736b4bc8be4d606fb3f7b7bd": {
          "model_module": "@jupyter-widgets/base",
          "model_module_version": "1.2.0",
          "model_name": "LayoutModel",
          "state": {
            "_model_module": "@jupyter-widgets/base",
            "_model_module_version": "1.2.0",
            "_model_name": "LayoutModel",
            "_view_count": null,
            "_view_module": "@jupyter-widgets/base",
            "_view_module_version": "1.2.0",
            "_view_name": "LayoutView",
            "align_content": null,
            "align_items": null,
            "align_self": null,
            "border": null,
            "bottom": null,
            "display": null,
            "flex": null,
            "flex_flow": null,
            "grid_area": null,
            "grid_auto_columns": null,
            "grid_auto_flow": null,
            "grid_auto_rows": null,
            "grid_column": null,
            "grid_gap": null,
            "grid_row": null,
            "grid_template_areas": null,
            "grid_template_columns": null,
            "grid_template_rows": null,
            "height": null,
            "justify_content": null,
            "justify_items": null,
            "left": null,
            "margin": null,
            "max_height": null,
            "max_width": null,
            "min_height": null,
            "min_width": null,
            "object_fit": null,
            "object_position": null,
            "order": null,
            "overflow": null,
            "overflow_x": null,
            "overflow_y": null,
            "padding": null,
            "right": null,
            "top": null,
            "visibility": null,
            "width": null
          }
        },
        "e6e425561bae48dca4dc9e6a00507a1d": {
          "model_module": "@jupyter-widgets/controls",
          "model_module_version": "1.5.0",
          "model_name": "DescriptionStyleModel",
          "state": {
            "_model_module": "@jupyter-widgets/controls",
            "_model_module_version": "1.5.0",
            "_model_name": "DescriptionStyleModel",
            "_view_count": null,
            "_view_module": "@jupyter-widgets/base",
            "_view_module_version": "1.2.0",
            "_view_name": "StyleView",
            "description_width": ""
          }
        },
        "e9d4a2a7fbf04d7db88c4001a3b1a8f4": {
          "model_module": "@jupyter-widgets/base",
          "model_module_version": "1.2.0",
          "model_name": "LayoutModel",
          "state": {
            "_model_module": "@jupyter-widgets/base",
            "_model_module_version": "1.2.0",
            "_model_name": "LayoutModel",
            "_view_count": null,
            "_view_module": "@jupyter-widgets/base",
            "_view_module_version": "1.2.0",
            "_view_name": "LayoutView",
            "align_content": null,
            "align_items": null,
            "align_self": null,
            "border": null,
            "bottom": null,
            "display": null,
            "flex": null,
            "flex_flow": null,
            "grid_area": null,
            "grid_auto_columns": null,
            "grid_auto_flow": null,
            "grid_auto_rows": null,
            "grid_column": null,
            "grid_gap": null,
            "grid_row": null,
            "grid_template_areas": null,
            "grid_template_columns": null,
            "grid_template_rows": null,
            "height": null,
            "justify_content": null,
            "justify_items": null,
            "left": null,
            "margin": null,
            "max_height": null,
            "max_width": null,
            "min_height": null,
            "min_width": null,
            "object_fit": null,
            "object_position": null,
            "order": null,
            "overflow": null,
            "overflow_x": null,
            "overflow_y": null,
            "padding": null,
            "right": null,
            "top": null,
            "visibility": null,
            "width": null
          }
        },
        "f5455d8b1b4c48dcb34708d81a98627c": {
          "model_module": "@jupyter-widgets/controls",
          "model_module_version": "1.5.0",
          "model_name": "HTMLModel",
          "state": {
            "_dom_classes": [],
            "_model_module": "@jupyter-widgets/controls",
            "_model_module_version": "1.5.0",
            "_model_name": "HTMLModel",
            "_view_count": null,
            "_view_module": "@jupyter-widgets/controls",
            "_view_module_version": "1.5.0",
            "_view_name": "HTMLView",
            "description": "",
            "description_tooltip": null,
            "layout": "IPY_MODEL_fffb14089cdf49d9bbb1f3b5b24d89be",
            "placeholder": "​",
            "style": "IPY_MODEL_e6e425561bae48dca4dc9e6a00507a1d",
            "value": " 4/4 [01:32&lt;00:00, 19.89s/it]"
          }
        },
        "fffb14089cdf49d9bbb1f3b5b24d89be": {
          "model_module": "@jupyter-widgets/base",
          "model_module_version": "1.2.0",
          "model_name": "LayoutModel",
          "state": {
            "_model_module": "@jupyter-widgets/base",
            "_model_module_version": "1.2.0",
            "_model_name": "LayoutModel",
            "_view_count": null,
            "_view_module": "@jupyter-widgets/base",
            "_view_module_version": "1.2.0",
            "_view_name": "LayoutView",
            "align_content": null,
            "align_items": null,
            "align_self": null,
            "border": null,
            "bottom": null,
            "display": null,
            "flex": null,
            "flex_flow": null,
            "grid_area": null,
            "grid_auto_columns": null,
            "grid_auto_flow": null,
            "grid_auto_rows": null,
            "grid_column": null,
            "grid_gap": null,
            "grid_row": null,
            "grid_template_areas": null,
            "grid_template_columns": null,
            "grid_template_rows": null,
            "height": null,
            "justify_content": null,
            "justify_items": null,
            "left": null,
            "margin": null,
            "max_height": null,
            "max_width": null,
            "min_height": null,
            "min_width": null,
            "object_fit": null,
            "object_position": null,
            "order": null,
            "overflow": null,
            "overflow_x": null,
            "overflow_y": null,
            "padding": null,
            "right": null,
            "top": null,
            "visibility": null,
            "width": null
          }
        }
      }
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
